/* This file contine the process captured buffer,
 * Currently,	1. output to a lot of jpeg pictures
 *		2. Output to a raw yuv422 format raw movie, [buggy]
 *
 * Author: kzjeef@gmail.com
 * Date  : 2008.11.23
 *
 */

#include "capture.h"

static void
put_jpeg_image (const char *image , size_t length)
{
	assert (image );
	assert (length > 0);
	int width	= _width;
	int height	= _height;
	int quality	= _quality;
	char fnamebuf[100];

	static int jpeg_count  = 0;
	CLEAR(fnamebuf);

#ifdef HAVE_LIBJPEG
	int y, x, line_width;
	JSAMPROW row_ptr[1];
	struct jpeg_compress_struct cjpeg;
	struct jpeg_error_mgr jerr;
	char *line;
	FILE *longxin_fd ;

#ifdef RGB4
	const int gap = 4;
#else
	const int gap = 3;
#endif
	
	sprintf(fnamebuf, "/tmp/capture_%d_file.jpg", jpeg_count);

	jpeg_count++;

	if ( jpeg_count < 15 )	/* drop first 15 pics , since it was black */
		return ;
	longxin_fd = fopen (fnamebuf,"w");

	if (longxin_fd < 0 ) printf ("open jpeg name %s error:%s ",fnamebuf,strerror(errno) );

	int size = width * gap* 8*10 ;
	line = malloc (size );
	if (verbose) fprintf(stderr, "line :0x%x with length %dKB\n", (unsigned int)line,size>>10);
	if (!line)
		return;
	cjpeg.err = jpeg_std_error(&jerr);
	jpeg_create_compress (&cjpeg);
	cjpeg.image_width = width;
	cjpeg.image_height= height;
	cjpeg.input_components = 3;
	cjpeg.in_color_space = JCS_RGB;
	jpeg_set_defaults (&cjpeg);
	jpeg_set_quality (&cjpeg, quality, TRUE);
	cjpeg.dct_method = JDCT_FASTEST;
//	jpeg_stdio_dest (&cjpeg, stdout);
	jpeg_stdio_dest (&cjpeg,longxin_fd);
	    
	jpeg_start_compress (&cjpeg, TRUE);

	row_ptr[0] = (JSAMPROW)line;
	line_width = width * gap;
	line_width = width * gap;

	if (verbose)
		printf ("width :%d height :%d line_width :%d\n",
			width,
			height,
			line_width);
			
	for ( y = 0; y < height; y++) {
		for (x = 0; x < line_width; x+=gap) {
			line[x]   = image[x+2];
			line[x+1] = image[x+1];
			line[x+2] = image[x];
		}
		jpeg_write_scanlines (&cjpeg, row_ptr, 1);
		image += line_width;
	}
	jpeg_finish_compress (&cjpeg);
	jpeg_destroy_compress (&cjpeg);
	free (line);
	fclose (longxin_fd);
#endif
}

inline void yuv2bitmap(unsigned char *bitmap, int y, int u, int v) 
{
	int r= 0, g= 0, b= 0;

	// u and v are +-0.5
	u -= 128;
	v -= 128;
	r = y + 1.370705 * v;
	g = y - 0.698001 * v - 0.337633 * u;
	b = y + 1.732446 * u;

	// Clamp to 0..1
	if (r < 0) r = 0;
	if (g < 0) g = 0;
	if (b < 0) b = 0;
	if (r > 255) r = 255;
	if (g > 255) g = 255;
	if (b > 255) b = 255;

	/* be aware that rgb format r byte g byte and b byte was in
	 * order like below, please don't try other order.*/
	bitmap[0] = r;
	bitmap[1] = g;
	bitmap[2] = b;
#ifdef RGB4
	/* RGB4 was not ued . */
	bitmap[3] = 0;
#endif

}


inline void conv_422_rgb_sub(const unsigned char *yuv, unsigned char *rgb1,unsigned char *rgb2) 
{
	int y1, y2, u, v;

	/* be aware that u ,v may exchange their position , because u
	 * and v 's position was not defined very clear , if your
	 * picture seems color not normal, you may try to exchang u
	 * and v 's order  */

	y1 = yuv[0];
	u = yuv[3];
	y2 = yuv[2];
	v = yuv[1];
	yuv2bitmap(rgb1, y1, u, v);
	yuv2bitmap(rgb2, y2, u, v);
}
	
static void
uyuv422_rgb (const void *puyuv, void *prgb, size_t lenyuv, size_t lenrgb) 
{
	/* uyuv have 2 bytes, and rgb request 4 bytes */
#ifdef RGB4
	int gap = 4;
#else
	int gap = 3;
#endif
	assert (((lenyuv >> 1) * gap) == lenrgb);
	int  j;
	void *rgb_offset = prgb;
	const void *yuv_offset = puyuv;
	for (j = 0; j < lenyuv; j += 4 ) {
		conv_422_rgb_sub(yuv_offset,
				 rgb_offset, 
				 rgb_offset + gap);
		yuv_offset += 4 ;
		rgb_offset += gap * 2;
	}
}

void
direct_jpeg(const void *p, size_t length, int count)
{
    char fnamebuf[50];
    sprintf(fnamebuf, "/tmp/dircapture_%d_file.jpg", count);

    if ( count < 15 )	/* drop first 15 pics , since it was black */
	return ;
    int file_fd = open(fnamebuf, O_RDWR | O_CREAT,NULL);

    int ret = write(file_fd, p, length);
    if (ret == length)
	printf("success write %s \n", fnamebuf);
    else
	printf("failed \%s :%s \n", fnamebuf, strerror(errno));
    
    close(file_fd);
}

#if 0 
int  overlay_fb (const void *p , size_t length)
{


    FBControl fbc;
    CLEAR(fbc);
    
    int fbd =  open(fb_dev, O_RDWR /* required */ | O_NONBLOCK, 0);
    if (verbose) printf(" opening framebuffer:%s \n", fb_dev);
    if (fbd < 0) {
	perror ("open error");
	return -1;
    }

    if (ioctl(fbd, FBIOGET_FSCREENINFO, &fbc.finfo)) {
	// Error reading fixed information.
	fprintf( stderr, "fbopen: reading fixed screen info error\n" );
	close( fbd );
	return FB_ERROR;
    }

    if (ioctl(fbd, FBIOGET_VSCREENINFO, &fbc.vinfo)) {
	// Error reading variable information.
	fprintf( stderr, "fbopen: reading variable screen info error\n" );
	close( fbd );
	return FB_ERROR;
    }
    
    fprintf( stderr, "bits_per_pixel=%d\n", fbc.vinfo.bits_per_pixel );
    fbc.vinfo.xres = 0;
    fbc.vinfo.yres = 0;
    fbc.vinfo.nonstd = (format << 20) | (ypos << 10) | xpos;

    if ( bpp > 0 ) {
	fbc.vinfo.bits_per_pixel = bpp;
	fprintf( stderr, "applying bpp=%d\n", bpp );
    }

    if ( 0 != ioctl( fbc.fd, FBIOPUT_VSCREENINFO, &fbc->vinfo ) ) {
	fprintf( stderr, "fbopen:vscreeninfo setup error, xr=%d, yr=%d, format=%d, ypos=%d, xpos=%d\n",
		 xr, yr, format, ypos, xpos );
	close( fd );
	return FB_ERROR;
    }

    int fd = fbc.fd;

    if (ioctl(fd, FBIOGET_VSCREENINFO, &fbc->vinfo)) {
	// Error reading variable information.
	fprintf( stderr, "fbremap: reading variable screen info error\n" );
	close( fd );
	return FB_ERROR;
    }

    int depth, lstep, xoff, yoff, dataoffset, mapsize;
    unsigned char *data;


    if ( fbc->map_data != 0 ) {
	munmap( fbc->map_data, fbc->mapsize );
    }
    if (verbose) {
	fprintf( stderr, "fb_var_screeninfo dump\n" );
	fprintf( stderr, "xres=%d, yres=%d, xres_virtual=%d, yres_virtual=%d\n", 
		 fbc->vinfo.xres,
		 fbc->vinfo.yres, 
		 fbc->vinfo.xres_virtual,
		 fbc->vinfo.yres_virtual);
	fprintf( stderr, "xoffset=%d, yoffset=%d, bits_per_pixel=%d, grayscale=%d\n", 
		 fbc->vinfo.xoffset, 
		 fbc->vinfo.yoffset, 
		 fbc->vinfo.bits_per_pixel, 
		 fbc->vinfo.grayscale);
	fprintf( stderr, "left_margin=%d, upper_margin=%d, smem_start=%lx, smem_len=%u\n", 
		 fbc->vinfo.left_margin, 
		 fbc->vinfo.upper_margin, 
		 fbc->finfo.smem_start, 
		 fbc->finfo.smem_len );
    }

    
    depth = fbc->vinfo.bits_per_pixel;
    lstep = fbc->finfo.line_length;
    xoff = fbc->vinfo.xoffset;
    yoff = fbc->vinfo.yoffset;
    // height = fbc->vinfo.yres;
    dataoffset = yoff * lstep + xoff * depth / 8;
    fprintf( stderr, "%s(%d):dataoffset=%d\n", __FUNCTION__, __LINE__, dataoffset );

    mapsize = fbc->finfo.smem_len;

    data = (unsigned char *)mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    // data += dataoffset;

    if ( (int) data == -1 ) {
	// Failed to map framebuffer device to memory.
	fprintf( stderr, "fbremap: mmap error\n" );
	close( fd );
	return FB_ERROR;
    }
	fbc->fd = fd;
	fbc->mapsize = mapsize;
	fbc->map_data = data;
	/* fbc->data = data + dataoffset; */
	fbc->data = data;
	fbc->height = fbc->vinfo.yres;
	fbc->width = fbc->vinfo.xres;
	fbc->bpp = depth;
	fbc->bpl = lstep;

	


/*     char * addr = mmap (NULL, */
/* 			    length, */
/* 			    PROT_READ | PROT_WRITE /\* required *\/, */
/* 			    MAP_SHARED /\* recommended *\/, */
/* 			    fbd, 0); */
/* 	if (verbose) printf ("after mmap: addr :%p \n", addr); */
/* 	if (addr == 0) */
/* 	    perror ("mmap error"); */
/* 	usleep(100); */
/* 	char * res = memcpy (addr, p, length); */

/* 	munmap(addr, length); */
/* 	if (verbose) printf("close framebuffer"); */
/* 	close(fbd); */

}
#endif


void
process_image  (const void * p, size_t length)
{
    static int count  = 0;
    if (dry_run)
	return ;

    assert(p);
    assert(length > 0);
    if (verbose)
	printf("%s : p: 0x%p, length: %d \n",
	       __FUNCTION__,
	       (unsigned int)p,
	       length);
	
    fputc ('.', stdout);
    fflush (stdout);


    if (!output_jpeg && fb_dev != 0) {
    }
    if (!output_jpeg && fb_dev == 0) {
	if (write (test_file_fd, p, length) != length)
	    perror ("write error");
    } else {
	    if (!direct_rgb)  {
		const size_t buf_size = (length>>1) * 3;
		char *buf = malloc (buf_size);
		memset (buf, 0, sizeof(buf));
		uyuv422_rgb (p, buf, length, buf_size);
		put_jpeg_image (buf , buf_size);
		free (buf);
	    } else  {
		put_jpeg_image(p, length);
	    }

//	direct_jpeg(p, length, count);
	}
	
    if (count++ == 5) {
	close (test_file_fd);
	exit (0);
    }
		
}

